#include "stdafx.h"
#include "ActiveObjectContainer.h"
#include "ActiveObject.h"
#include "NullDeleter.h"
#include "LocalEndpoint.h"


namespace RoomFW {

ActiveObjectContainer::ActiveObjectContainer(size_t portCount) :
	inEndPointMap_(portCount),
	inReplyEndPointMap_(portCount),
	outEndPointMap_(portCount),
	outReplyEndPointMap_(portCount)
{
	innerWall_.setOwner(this);
}

ActiveObjectContainer::~ActiveObjectContainer(void)
{
	// This is a warning for derived objects to ensure that their children have
	// been cleaned up.  It doesn't necessarily indicate an error, but there's
	// no harm in requiring derived classes to call clearChildList() in their
	// destructor as a reassurance that children have been cleaned up.
	ASSERT(childList_.empty());
	inReplyEndPointMap_.clearAll();
	outReplyEndPointMap_.clearAll();
}

bool ActiveObjectContainer::start(uint32_t timeout)
{
	return this->childListWait(&IActiveObject::start, timeout);
}

bool ActiveObjectContainer::stop(uint32_t timeout)
{
	return this->childListWait(&IActiveObject::stop, timeout);
}

bool ActiveObjectContainer::wait(uint32_t timeout)
{
	return this->childListWait(&IActiveObject::wait, timeout);
}

bool ActiveObjectContainer::isRunning(uint32_t timeout)
{
	return this->childListWait(&IActiveObject::isRunning, timeout);
}

bool ActiveObjectContainer::childListWait(AOWait fn, uint32_t timeout)
{
	bool retVal = true;
	if ((INFINITE != timeout) && !childList_.empty())
	{
		timeout /= childList_.size();
	}
	BOOST_FOREACH(IActiveObjectSP child, childList_)
	{
		if (!(child.get()->*fn)(timeout))
		{
			retVal = false;
		}
	}
	return retVal;
}

bool ActiveObjectContainer::isErrored()
{
	bool retVal = false;
	BOOST_FOREACH(IActiveObjectSP child, childList_)
	{
		if (child->isErrored())
		{
			retVal = true;
			break;
		}
	}
	return retVal;
}

std::string ActiveObjectContainer::errorMessage()
{
	std::string retVal;
	BOOST_FOREACH(IActiveObjectSP child, childList_)
	{
		if (child->isErrored())
		{
			retVal = child->errorMessage();
			break;
		}
	}
	return retVal;
}

ErrorHandling::IErrorSP ActiveObjectContainer::getError()
{
	ErrorHandling::IErrorSP retVal;
	BOOST_FOREACH(IActiveObjectSP child, childList_)
	{
		if (child->isErrored())
		{
			retVal = child->getError();
			break;
		}
	}
	return retVal;
}
IEndPointSP ActiveObjectContainer::getEndPoint(PortIndex::PrimaryIndex portIndex)
{
	return this->getEndPoint(outReplyEndPointMap_, portIndex, false);
}

bool ActiveObjectContainer::setEndPoint(PortIndex& portIndex, IEndPointSP endPoint)
{
	return this->setEndPoint(outEndPointMap_, portIndex, endPoint);
}

bool ActiveObjectContainer::clearEndPoint(const PortIndex& portIndex)
{
	ASSERT(portIndex.port() < outEndPointMap_.size());
	ASSERT(portIndex.replication() < outEndPointMap_.replicationSize(portIndex.port()));
	return outEndPointMap_.clear(portIndex.port(), portIndex.replication());
}

void ActiveObjectContainer::put(RoomMessageSP message)
{
	IEndPointSP ep = inEndPointMap_.get(message->portIndex().port(), message->portIndex().replication());
	if (0 == ep)
	{
		throw SendMessageError(
				this->getObjectName(),
				message->portIndex(),
				message->messageID(),
				message->payload(),
				SendMessageError::SMER_PORT_NOT_BOUND);
	}
	ep->acceptMessage(message->messageID(), message->releasePayload(), message->senderName(), message->replyEndpoint());
}

IEndPointSP ActiveObjectContainer::getInnerWallEndPoint(PortIndex::PrimaryIndex portIndex)
{
	return this->getEndPoint(inReplyEndPointMap_, portIndex, true);
}

bool ActiveObjectContainer::setInnerWallEndPoint(PortIndex& portIndex, IEndPointSP endPoint)
{
	return this->setEndPoint(inEndPointMap_, portIndex, endPoint);
}

IEndPointSP ActiveObjectContainer::getEndPoint(EndpointMap& endpoints, PortIndex::PrimaryIndex portIndex, bool innerWall)
{
	// Trying to bind an invalid port index is a programming error, hence the assertion.
	ASSERT(portIndex < endpoints.size());	

	// The current implementation does not support replicating relay ports.
	ASSERT(0 == endpoints.replicationCount(portIndex));

	LocalEndPointSP retVal;
	if (0 == endpoints.replicationCount(portIndex))
	{
		retVal.reset(new LocalEndPoint((innerWall ? static_cast<IActiveObject*>(&innerWall_) : this), portIndex));
		size_t replication = endpoints.add(portIndex, retVal);
		if (PortIndex::REPLICATION_BROADCAST != replication)
		{
			retVal->setReplication(replication);
		}
		else
		{
			retVal.reset();
		}
	}
	return retVal;
}

bool ActiveObjectContainer::setEndPoint(EndpointMap& endpoints, PortIndex& portIndex, IEndPointSP endPoint)
{
	// Trying to bind an invalid port index is a programming error, hence the assertion.
	ASSERT(portIndex.port() < endpoints.size());	

	// The current implementation does not support replicating relay ports.
	ASSERT(0 == endpoints.replicationCount(portIndex.port()));

	bool retVal = false;
	if (0 == endpoints.replicationCount(portIndex.port()))
	{
		size_t replication = endpoints.add(portIndex.port(), endPoint);
		if (PortIndex::REPLICATION_BROADCAST != replication)
		{
			portIndex.replication(replication);
			retVal = true;
		}
	}
	return retVal;
}

bool ActiveObjectContainer::clearInnerWallEndPoint(PortIndex portIndex)
{
	ASSERT(portIndex.port() < inEndPointMap_.size());
	ASSERT(portIndex.replication() < inEndPointMap_.replicationSize(portIndex.port()));
	return inEndPointMap_.clear(portIndex.port(), portIndex.replication());
}

void ActiveObjectContainer::sendMessage(PortIndex portIndex, MessageID messageID, IPayloadSP payload, const char* senderName, IEndPointSP replyEndpoint) const
{
	IEndPointSP ep = outEndPointMap_.get(portIndex.port(), portIndex.replication());
	if (0 == ep)
	{
		throw SendMessageError(
				this->getObjectName(),
				portIndex,
				messageID,
				payload,
				SendMessageError::SMER_PORT_NOT_BOUND);
	}
	ep->acceptMessage(messageID, payload, senderName, replyEndpoint);
}

IActiveObjectSP ActiveObjectContainer::getInnerWall(void)
{
	return IActiveObjectSP(&innerWall_, NullDeleter<IActiveObject>());
}

ActiveObjectContainer::InnerWall::InnerWall(void) :
	owner_(0)
{
}

ActiveObjectContainer::InnerWall::~InnerWall(void)
{
}

void ActiveObjectContainer::InnerWall::setOwner(ActiveObjectContainer* owner)
{
	owner_ = owner;
}

IEndPointSP ActiveObjectContainer::InnerWall::getEndPoint(PortIndex::PrimaryIndex portIndex)
{
	return owner_->getInnerWallEndPoint(portIndex);
}

bool ActiveObjectContainer::InnerWall::setEndPoint(PortIndex& portIndex, IEndPointSP endPoint)
{
	return owner_->setInnerWallEndPoint(portIndex, endPoint);
}

bool ActiveObjectContainer::InnerWall::clearEndPoint(const PortIndex& portIndex)
{
	return owner_->clearInnerWallEndPoint(portIndex);
}

IContractCheckerSPC ActiveObjectContainer::InnerWall::getContractChecker(PortIndex portIndex) const
{
	return owner_->getContractChecker(portIndex)->getConjugate();
}

void ActiveObjectContainer::InnerWall::put(RoomMessageSP message)
{
	owner_->sendMessage(message->portIndex(),
						message->messageID(),
						message->releasePayload(),
						message->senderName(),
						message->replyEndpoint());
}

void ActiveObjectContainer::removeChild(IActiveObjectSP child)
{
	for ( ChildList::iterator pElement = childList_.begin(); pElement != childList_.end(); pElement++ )
	{
		if ( *pElement == child )
		{
			childList_.erase(pElement);
			break;
		}
	}
}


}
